home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Nebula 1
/
Nebula One.iso
/
Utilities
/
Converters
/
Convert_FONT
/
Source
/
shared.subproj
/
RCS
/
ConvertController.m,v
< prev
next >
Wrap
Text File
|
1995-06-12
|
68KB
|
1,998 lines
head 1.4;
branch ;
access ;
symbols beta10:1.3;
locks death:1.4;
comment @@;
1.4
date 93.04.04.23.44.18; author death; state Exp;
branches ;
next 1.3;
1.3
date 93.01.10.15.07.50; author death; state Exp;
branches ;
next 1.2;
1.2
date 92.07.26.13.57.36; author death; state Exp;
branches ;
next 1.1;
1.1
date 92.07.26.13.51.42; author death; state Exp;
branches ;
next ;
desc
@Basic and primitive convert controller object
@
1.4
log
@Sun Apr 4 23:44:18 PDT 1993
@
text
@#import "ConvertController.h"
#import "File.h"
#import "AbstractConverter.h" // mainly to stop annoying 'I don't know about this runtime method call'
#import <stdio.h>
#import <stdlib.h>
#import <string.h>
#import <appkit/Panel.h>
#import <appkit/Window.h> // for manipulations of ProgressWindow
#import <appkit/Matrix.h>
#import <appkit/Cell.h>
#import <appkit/SavePanel.h>
#import <appkit/OpenPanel.h>
#import <libc.h> //for getwd
#import <sys/param.h>
//
// Include the following 2 for drag and drop facility, plus for IAC communications.
//
#import "ConvertListener.h"
#import "ConvertSpeaker.h"
#import <appkit/Application.h> // To get NXApp (I thought that is what defaults was...)
#import <appkit/publicWraps.h> // for NXConvertWinNumToGlobal
#import <appkit/graphics.h> // For NXPing
#import <appkit/publicWraps.h> // For NXBeep
@@implementation ConvertController
/*============================================================*\
The following routines are all for the application delegate responsibilities of this
class. Basically dealing with starting up, shutting down, and with allowing the
user to double click on a file in the workspace and have it open up, and setting up
for providnig communications between applications
\*============================================================*/
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Routine: appWillInit:
// Parameters: our caller
// Returns: self
// Stores: none
// Description:
// This is used to store a specialized speaker and listener in the application.
// these specialized objects are subclasses of the normal speaker and listener
// classes, and together implement a method to allow the caller to instruct this
// class to convert a file.
// Bugs:
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- appWillInit: sender
{
Instance mySpeaker;
Instance myListener;
//
// Set up the conversion-aware speaker
//
mySpeaker = [[ConvertSpeaker alloc] init];
[mySpeaker setDelegate:NXApp];
[NXApp setAppSpeaker:mySpeaker];
//
// Set up the conversion-aware listener
//
myListener = [[ConvertListener alloc] init];
[myListener setDelegate:NXApp];
[NXApp setAppListener:myListener];
return self;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Routine: appDidInit:
// Parameters: our caller
// Returns: self
// Stores: none
// Description:
// This is for when this class acts as a delegate to the application. It allows it to
// do some final initialization stuff. The big thins is to allow us to register out
// window so the user will be able to drag icons over it, and thus serve as the
// drag-and-drop facilities that we support. This later causes iconEntered: and
// iconReleasedAt::ok: messages to be sent later.
// The 2.x code for the drag-and-drop was mainly copied from DrawDocument.
// The 3.x code is copied straight from NeXT's Window.rtf help doc. The window
// registration for 3.0 is much more straight forward.
// Bugs:
// Perhaps I should be using appDidBecomeActive here some?
// History
// 93.01.24 djb Modified so will make use of NS 3.0 drag and drop
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- appDidInit: sender
{
#if ((NSmajor == 2) || (DoNS3DragNDrop == 0))
//
// Allocate a listener instance, and store as one of our instance variables.
// Set it up as using our private port, and have the app's speaker register
// the window and port.
//
listener = [[Listener allocFromZone:[self zone]] init]; // from MY zone?
[listener setDelegate:self]; // We'll now get the messages from the workspace
[listener usePrivatePort];
[listener addPort];
#endif
[self allowDragAndDrop]; // do nothing special for NS 3.0 style
//
// Force the window the front, and give it's miniwindow an icon.
//
[ProgressWindow setMiniwindowIcon: "Application"];
[ProgressWindow makeKeyAndOrderFront:self];
//
// 93.01.08 djb Whoops. Wasn't disabling parts of edit menu right off as needs to be
//
[cutCommand setEnabled: NO];
[pasteCommand setEnabled: NO];
[spellingCommand setEnabled: NO];
[checkSpellingCommand setEnabled: NO];
return self;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Routine: appWillTerminate:
// Parameters: our caller
// Returns: self
// Stores: none
// Description:
// This serves tobalance the init above!
// Mainly, it unregisters our window information that was used to allow the
// workspace to have the user drag folders over our window!
// This code is basically copied directly from DrawDocument.m =)
// Bugs:
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- appWillTerminate: sender
{
[self refuseDragAndDrop];
if (listener != NullInstance)
[listener free];
return self;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Routine: appAcceptsAnotherFile:
// Parameters: our caller
// Returns: self
// Stores: none
// Description:
// If we can accept another file to be opened, we return YES. If we can't we respond
// NO. For now, we always return that we'll accept another file. This just ends
// up queueing files waiting to be converted.
// Bugs:
// To be honest, this makes me a bit uncomfortable. I feel as though I should be
// keeping some kinda lock and returning NO if I'm presently converting
// something. yet, if I'm converting something, nothing else should be able to
// be doing any execution here, so I guess it doesn't matter.
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- (BOOL)appAcceptsAnotherFile:sender
{
return YES;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Routine: app:openFile:type:
// Parameters: The name of the file to be converted
// It's extension
// Returns: a flag indicating whether we can deal with the file
// Stores: none
// Description:
// The user double clicked on a file in the workspace, and now we are asked to open it.
// All this does, though, is pass the file information on to the first level conversion
// method and let it try to deal with it.
// Bugs:
// Might it be nicer to play a game, like below, where we do a 'perform' to do the
// conversion after we return YES to the caller? That begins to seem a bit
// dangerous to me. still.....
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- (int)app:sender openFile:(const char *)filename type:(const char *)aType
{
CString tempString;
//
// Ask the convert controlling object to convert this particular file
//
[self ConvertThisFile: filename];
//
// Check if the file was converted alright...
//
if ([self GetErrorCode] == ERR_OK)
{
[SourceTitle setStringValue: "Converted:"];
[Status setStringValue: "The conversion is done."];
[self StoreErrorCode: ERR_OK AndText: "Conversion process done"];
}
else
{
[SourceTitle setStringValue: "Failed:"];
tempString = [self GetErrorText];
[Status setStringValue: tempString];
FreeCString(tempString);
}
return YES; // Always report that it was converted ok
}
/*============================================================*\
The following routines are all for the drag-and-drop facilities which allow the
user to drag files from the workspace, and drop them over the main window of
the application. Fun with InterProcessCommuniction. =)
\*============================================================*/
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Routine: iconEntered:at:iconWindow:iconX:iconY:iconWidth:iconHeight:pathList:
// Parameters: various, mostly not important at the moment
// Returns: 0 if successfull, something else if not
// Stores: none
// Description:
// When the user drags a clump of one of more files over our window, we get
// this message. We store away the list of files, because we are not given it
// later on. =( Note that we first free any pending set of file paths in the
// extremely unlikely condition that there is one already there, and then
// copy the list of paths for use in iconReleasedAt: below
// Bugs:
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- (int)iconEntered:(int)windowNum at:(double)x :(double)y
iconWindow:(int)iconWindowNum iconX:(double)iconX iconY:(double)iconY
iconWidth:(double)iconWidth iconHeight:(double)iconHeight
pathList:(char *)pathList
{
if (filePaths != NullCString)
FreeCString(filePaths);
filePaths = NewCString(strlen(pathList));
strcpy(filePaths, pathList);
return 0;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Routine: iconMovedTo::
// Parameters: n/a
// Returns: n/a
// Stores: n/a
// Description:
// I'm NOT implimenting this, though it might still be nice if we wanted some
// kinda graphical effect, or if we could enforce dropping in only a particular
// area (I like this idea, rather than the generic droppoing we have now...
// (heh. drop on the NeXT icon to convert it TO NeXt, drop it on the Mac
// to convert TO mac. =) However, the below code demonstrates that one can
// not change the little-copy-cursor-icon-thingie based on a return value
// Bugs:
// None, of course, since it is commented out, unless you count that I've grotesquely put it
// all on one line so one doesn't mistake that it is in fact commented out for now.
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/*- (int)iconMovedTo:(double)x :(double)y { if ((x > 100) || (y > 100)) {NXBeep(); return 0;} else return 1; } */
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Routine: iconReleasedAt::ok:
// Parameters: x and y coords of the dropping point
// Returns: 0 if successfull, something else if not
// a ptr to a boolean flag returned by reference
// Stores: none
// Description:
// When the user droppes the files, we inform the workspace manager that we
// can deal with them (always). We then ask ourselves to do something in
// some fraction of a second.
// The reason for all this is simple: processing the files is apt to take some time,
// and we don't want that icon hanging on the window while we do so. So,
// we gotta return soon. Fact is, we also want to be liberal about what kind
// of files to accept, since in general many of these conversion programs may not
// have files with 'proper' extensions (e.g. what isthe extension for a Mac font?).
// So, we tell the workspace all is well, so the icon goes away. Soon, we get
// reminded to ProcessDroppedFiles, which will go through and actually start
// the processing.
// Bugs:
// As with some of the routines above, a bit of me is concerned that if this code is
// executed while this instance is also doing some conversion, trouble will result.
// but, then, of course, this isn't a multi-threaded thing, so I shouldn't need to
// worry about that. Why does my mind keep trying to think in terms of a
// processing architecture that doesn't exist yet?
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- (int)iconReleasedAt:(double)x :(double)y ok:(int *)flag
{
[self perform: @@selector(ProcessDroppedFiles)
with: self
afterDelay: 100
cancelPrevious: NO];
*flag = YES;
return 0;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Routine: iconExitedAt::
// Parameters: The x and y coordinates at which the icon left the window
// Returns: 0 if successfull, something else if not
// Stores: none
// Description:
// If the user drags the icon off the window, we just free the string of
// paths and reutrn.
// Bugs:
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- (int)iconExitedAt:(double)x :(double)y
{
if (filePaths != NullCString)
{
FreeCString(filePaths);
filePaths = NullCString;
}
return 0;
}
#if (NSmajor == 3)
/*============================================================*\
The following routines are NeXTSTEP 3.0 routines that will allow for drag and drop
of filenames. NOTE that these are equivalent to the routines above, and that both
are being maintained so I can recompile easily under 2.1 if needed.
NOTE! For this to work, the instance that this is must be designated the delagate
of the progresswindow!
It Might be more efficient, by the way, instead of registering and undregistering the
accepted types all the time, to instead set a flag and return NX_DragOperationNone.
\*============================================================*/
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Routine: draggingEntered:
// Parameters: the caller
// Returns: NX_DragOperationCopy
// Stores: none
// Description:
// Aside from telling the caller that we'll happily convert whatever files we're
// being asked about, this also gets and stores the tab delimited list of file names
// for potential later processing.
// Bugs:
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- (NXDragOperation)draggingEntered:(id)sender
{
Instance filenamePasteboard;
CString names;
int thelength;
filenamePasteboard = [sender draggingPasteboard];
if (filePaths != NullCString)
FreeCString(filePaths);
[filenamePasteboard readType: NXFilenamePboardType data: &names
length: &thelength];
filePaths = NewCString(thelength);
strcpy(filePaths, names);
[filenamePasteboard deallocatePasteboardData: names length: thelength];
return NX_DragOperationCopy;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Routine: draggingUpdated:
// Parameters: the caller
// Returns: NX_DragOperationCopy
// Stores: none
// Description:
// This merely indicates that we'll be happy to try to convert the specified
// filenames. We aren't particular aboout what kinds of files they are. =)
// Bugs:
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- (NXDragOperation)draggingUpdated:(id)sender
{
return NX_DragOperationCopy;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Routine: prepareForDragOperation:
// Parameters: the caller
// Returns: YES
// Stores: none
// Description:
// We will always do the drag operation.
// Bugs:
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- (BOOL)prepareForDragOperation:(id)sender
{
return YES;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Routine: performDragOperation:
// Parameters: the caller
// Returns: YES
// Stores: none
// Description:
// Convert the file(s). Indicate that we could.
// Bugs:
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- (BOOL)performDragOperation:(id)sender
{
[self perform: @@selector(ProcessDroppedFiles)
with: self
afterDelay: 100
cancelPrevious: NO];
return YES;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Routine: concludeDragOperation:
// Parameters: the caller
// Returns: self
// Stores: none
// Description:
// Do nothing, as there's nothing to clean up, really (we could free the copy of
// the pathnames, but there's no real urgent need).
// Bugs:
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- concludeDragOperation:(id)sender
{
return self;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Routine: draggingExited:
// Parameters: the caller
// Returns: returns self
// Stores: none
// Description:
// If the user drags the icon off the window, we just free the string of
// paths and reuturn
// Bugs:
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- draggingExited:(id)sender
{
if (filePaths != NullCString)
{
FreeCString(filePaths);
filePaths = NullCString;
}
return self;
}
#endif
/*============================================================*\
The following routines are various basic utility routines used by the above
methods. In short: toggle drag-and-drop on and off, and process the file names
when/if they are actually dropped.
\*============================================================*/
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Method: refuseDragAndDrop
// Parameters: none
// Returns: self
// Stores: none
// Description:
// This handy method is used to turn off our request to allow files to be dropped
// over the application's window. Clearly, it has conditionally compiled sections
// for NS 3.0 and 2.x style drag and drop
// Bugs:
// History:
// 93.01.24 djb Added support for 3.0 style drag and drop
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- refuseDragAndDrop
{
#if ((NSmajor == 2) || (DoNS3DragNDrop == 0))
unsigned int windowNum;
Instance speaker = [NXApp appSpeaker];
if (listener != NullInstance)
{
[speaker setSendPort: NXPortFromName(NX_WORKSPACEREQUEST, NULL)];
NXConvertWinNumToGlobal([ProgressWindow windowNum], &windowNum);
[speaker unregisterWindow: windowNum];
}
#else
[ProgressWindow unregisterDraggedTypes];
#endif
return self;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Method: allowDragAndDrop
// Parameters: none
// Returns: self
// Stores: none
// Description:
// This turns on the ability to have files droped over our window so we can
// process them. It has code fro both NS 2.x and 3.x style implementations
// Bugs:
// History:
// 93.01.24 djb Added support for 3.0 style drag and drop
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- allowDragAndDrop
{
#if ((NSmajor == 2) || (DoNS3DragNDrop == 0))
unsigned int windowNum;
Instance speaker = [NXApp appSpeaker];
NXConvertWinNumToGlobal([ProgressWindow windowNum], &windowNum);
[speaker setSendPort: NXPortFromName(NX_WORKSPACEREQUEST, NULL)];
[speaker registerWindow:windowNum toPort:[listener listenPort]];
#else
//
// We must tell the window to be ready for some dragged file names
//
const char *dataType[] = {NXFilenamePboardType};
[ProgressWindow registerForDraggedTypes: dataType count:1];
#endif
return self;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Routine: ProcessDroppedFiles
// Parameters: none
// Returns: self
// Stores: none
// Description:
// There is, presumably, string of file names waiting to be processed. We
// should process them one at a time, then. We make a copy of the file name
// list, and then repeatedly locate the and start and end of each file path name
// in the list (they're tab delimited). For each that we find, we process it.
// Bugs:
// We could almost certainly munge the file path string directly, but I feel
// safer treating it 'right'.
// If any of the files have tabs in their names, I have no idea what will or will
// have happened.
// I chose not to nuke the filePaths at when done here. This might not be good?
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- ProcessDroppedFiles
{
CString allpaths,
nextpath,
location;
CString tempString;
allpaths = NewCString(strlen(filePaths));
strcpy(allpaths, filePaths);
nextpath = allpaths;
do
{
//
// Find the next tab. If we found it, put a null there. If we did not,
// then this is the end of allpaths. We still have the last file name to
// process, so we keep going. In all cases, convert the file, set up for
// the next conversion (if any). Check whether we reached the end, and
// if so quit.
//
location = strchr(nextpath, '\t');
if (location != EndOfCString)
location[0] = EndOfCString;
[self ConvertThisFile: nextpath];
//
// Check if the file was converted alright...
//
if ([self GetErrorCode] == ERR_OK)
{
[SourceTitle setStringValue: "Converted:"];
[Status setStringValue: "The conversion is done."];
[self StoreErrorCode: ERR_OK AndText: "Conversion process done"];
}
else
{
[SourceTitle setStringValue: "Failed:"];
tempString = [self GetErrorText];
[Status setStringValue: tempString];
FreeCString(tempString);
}
nextpath = location+1;
}
while (location != EndOfCString);
FreeCString(allpaths);
return self;
}
/*============================================================*\
The following methods are intended to be used to allow a limited dialog as the
'slave' of another app that wants us to convert a file.
\*============================================================*/
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Routine: msgQuit
// Parameters: An integer that we fill in YES if we work (we always work =)
// Returns: self
// Stores: none
// Description:
// This will set up a delayed message to the app to have it quit. We allow PLENTY
// of time for us to quit and retun to the caller before that method gets invoked. If
// we didn't provide enough time (as, for instance the Draw demo app does not), then
// the caller will hang until time out because we would never return from the call.
// Bugs:
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- (int)msgQuit:(int *)flag
{
[NXApp perform:@@selector(terminate) with: NXApp afterDelay: 300
cancelPrevious: NO];
*flag = YES;
return 0;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Routine: msgConvert:To:
// Parameters: The name of the file to convert from, and the name of the file to convert to.
// Returns: self
// Stores: none
// Description:
// This provides a way for an other application to call us, and get into our
// main conversion routines below. This takes the two files, opens them, and jumps
// to the main conversio method. If either fails to open, we abort the process.
// However, the caller never learns of this.
// These file names, basically, should be complete pathnames
// Bugs:
// I still don't understand how we we can be sure that these strings haven't been
// freed by the time we get to them...
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- msgConvert: (char *) sourceFile To: (char*) destFile
{
CString sourceName = NewCString(strlen(sourceFile));
CString destName = NewCString(strlen(destFile));
CString dirname;
CString filename;
CString tempString;
Instance source;
Instance destination;
strcpy(sourceName, sourceFile);
strcpy(destName, destFile);
//
// Get ourselves out of the way of the caller.
// Bug: We still display our window briefly.
//
[NXApp hide: self];
source = [self openSourceFile: sourceName];
if ([self GetErrorCode] == ERR_OK)
{
[StatusTitle setStringValue: "Status:"];
[Status setStringValue: "Converting"];
[SourceTitle setStringValue: "Converting:"];
[SourcePathTitle setStringValue: "In:"];
filename = [source GetFilename];
dirname = [source GetDirectory];
[SourceFileName setStringValue: filename];
[SourcePath setStringValue: dirname];
FreeCString (filename);
FreeCString (dirname);
destination = [self openDestFile: destName];
if ([self GetErrorCode] == ERR_OK)
{
[DestTitle setStringValue: "To:"];
[DestPathTitle setStringValue: "In:"];
filename = [destination GetFilename];
dirname = [destination GetDirectory];
[DestFileName setStringValue: filename];
[DestPath setStringValue: dirname];
FreeCString (filename);
FreeCString (dirname);
[self DoConversionFrom: source To: destination];
if ([self GetErrorCode] == ERR_OK)
{
[SourceTitle setStringValue: "Converted:"];
[Status setStringValue: "The conversion is done."];
}
else
{
[SourceTitle setStringValue: "Failed:"];
tempString = [self GetErrorText];
[Status setStringValue: tempString];
FreeCString(tempString);
}
}
else
{
[SourceTitle setStringValue: "Failed:"];
[Status setStringValue: "Could not open the destination file."];
[source free];
}
}
else
{
[SourceTitle setStringValue: "Failed:"];
[Status setStringValue: "Could not open the source file."];
}
FreeCString(sourceName);
FreeCString(destName);
return 0;
}
/*============================================================*\
The rest of the methods here do the stuff that is the focus of the application.
Specifically, we init, deal with user preference settings, set the indicator on the
screen to indicate how far done the conversion is, open and close files and check
that the file to be converted is OK.
\*============================================================*/
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Routine: init
// Parameters: none
// Returns: self
// Stores: none
// Description:
// This overrides the defalut init method. But it does little else.
// Other initializations done in AppDidInit, above...
// Note that subclasses should OVERRIDE this
// Bugs:
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- init
{
CString temppath,
fullpath,
tempPos;
[super init];
destIsDead = NO;
//
// Clear, if necessary, the string of file names
//
filePaths = NullCString;
//
// Provide silly defaul strings.
//
ConversionString = "Converting a file to another";
SourcePrompt = "a file:";
DestPrompt = "another file:";
DestExtension = ".a";
DefaultsOwner = "I have no defaults";
//
// Locate the path where the application exists (if the app is a foo.app type appwrapper,
// this includes the foo.app directory).
// (if NXArgv doesn't have a full path, build one using getwd)
//
AppHome = NewCString(MAXPATHLEN);
strcpy(AppHome, NXArgv[0]);
if (AppHome[0] != '/')
{
//
// We musta been started up from the command line, so use our working
// directory to constructe the rest of the path.
//
temppath = NewCString(MAXPATHLEN);
getwd(temppath);
fullpath = NewCString(strlen(temppath) + 1 + strlen(AppHome));
sprintf(fullpath, "%s/%s", temppath, AppHome);
FreeCString(AppHome);
AppHome = fullpath;
FreeCString(temppath);
}
//
// Now, remove the application's name from the end of the path.
//
tempPos = strrchr(AppHome, '/');
tempPos[0] = EndOfCString;
return self;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Routine: free
// Parameters: none
// Returns: self
// Stores: none
// Description:
// This merely cleans up after ourselves.
// Bugs:
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- free
{
FreeCString(AppHome);
return self;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Routine: GetBoolPref
// Parameters: The preference that we are to retrieve the value for
// Returns: YES or NO, depending on the value of the preference
// Stores: None
// Description:
// This is used to lookup the value for a preference in the defaults database,
// and return a Boolean value to the caller. It's really just provided as a
// shortcut to the several lines of code needed to retrieve the value, compare
// it to a string value of YES or NO, and then return the appropriate result.
// Bugs:
// If anything goes wrong, the user will get a NO value, and not an error code.
// Should I be disposing of the string that I get back?
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- (Boolean) GetBoolPref: (ConstCString) thePreference
{
ConstCString theValue;
Boolean result;
theValue = NXGetDefaultValue(DefaultsOwner, thePreference);
if (strcmp(theValue, "YES") == 0)
result = YES;
else
result = NO;
return result;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Routine: SetBoolPref:To:
// Parameters: The preference that we are to retrieve the value for
// The Boolean value to assign it
// Returns: self
// Stores: None
// Description:
// This is used to store a boolean value for a preference in the defaults database.
// Bugs:
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- SetBoolPref: (ConstCString) thePreference To: (Boolean) value
{
if (value == YES)
NXWriteDefault(DefaultsOwner, thePreference, "YES");
else
NXWriteDefault(DefaultsOwner, thePreference, "NO");
return self;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Routine: SetPref:To:
// Parameters: The preference that we are to retrieve the value for
// The string value to assign it
// Returns: self
// Stores: None
// Description:
// This is used to store a string value for a preference in the defaults database.
// Bugs:
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- SetPref: (ConstCString) thePreference To: (CString) value
{
NXWriteDefault(DefaultsOwner, thePreference, value);
return self;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Routine: GetPref:
// Parameters: The preference that we are to retrieve the value for
// Returns: A copy of the preference string.
// Stores: None
// Description:
// This retrieves a string from the defaults database associated with the name
// thePreference, makes a copy of it, and returns it.
// Bugs:
// History:
// 93.02.15 djb Added for the rtf converter's new three option preference.
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- (CString) GetPref: (ConstCString) thePreference
{
ConstCString theValue;
CString result;
theValue = NXGetDefaultValue(DefaultsOwner, thePreference);
result = NewCString(strlen(theValue));
strcpy(result, theValue);
return result;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Routine: displayPreferences:
// Parameters: the object that called us
// Returns: self
// Stores: none
// Description:
// This method is indended to be overridden. The subclass would just do whatever
// it needs to do to display preferences for the user..
// Bugs:
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-displayPreferences: target
{
return self;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Routine: SetPercentageDone:
// Parameters: A new percentage to display
// Returns: self
// Stores: none
// Description:
// This allows a calling object to set the percentage of conversion done so far.
// Note that we only actually ask the percentage meter to display itself in
// in increments of 5%, simply because re-drawing after every percent, for
// example, is EXTREMELY slow.
// Bugs:
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- SetPercentageDone: (Real) percentage
{
if (percentage > lastPercent+5)
{
[ProgressMeter SetTo: percentage];
lastPercent = percentage;
NXPing();
}
return self;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Routine: openSource and Dest files
// Parameters: the full path of the file to be converted.
// Returns: the file object
// Stores: errors
// Description:
// These tiny methods are provided so a subclass can override what kind of
// file gets opened for the source or the dest, without overriding the whole
// of the conversion methods. Note that they should always open a subclass
// of File, though.
// Bugs:
// Modifications
// 92.11.27 djb Changed CreateAndOpenFor: to ClearAnd.... Any time this method
// is called within the framework of this class, the user has already
// specified they definitely want to use this file, even if it already
// exists, so we want to just clear the file if it exists already.
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- openDestFile: (roCString) theFile
{
Instance fileInstance;
[self ResetResults];
fileInstance = [[File alloc] initAndUse: theFile];
if ([fileInstance GetErrorCode] == ERR_OK)
[fileInstance ClearAndOpenFor: FILE_WRITE];
if ([fileInstance GetErrorCode] != ERR_OK)
{
[self StoreErrorCode: ERR_CREATEFAILED
AndText: "We failed to create the file"];
[fileInstance free];
fileInstance = NullInstance;
}
return fileInstance;
}
- openSourceFile: (roCString) theFile
{
Instance fileInstance;
[self ResetResults];
fileInstance = [[File alloc] initAndUse: theFile];
if ([fileInstance GetErrorCode] == ERR_OK)
[fileInstance OpenExistingFor: FILE_READ];
if ([fileInstance GetErrorCode] != ERR_OK)
{
[self StoreErrorCode: ERR_OPENFAILED
AndText: "We failed to open the file"];
[fileInstance free];
fileInstance = NullInstance;
}
return fileInstance;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Routine: closeSource and Dest files
// Parameters: the file object to be closed
// Returns: self
// Stores: errors
// Description:
// These Little methods are used to undo the work of the above open methods.
// they close the file objects, and free them. Like the above, they are pretty
// simple and generic, and will probably be overridden by many converters.
// Note that the dest file closing method has a boolean flag. If true, we also delete
// the file (presumably something went wrong.
// Bugs:
// History
// 93.01.01 djb Added tempString and freeing of it, so as not to leak memory.
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- closeSourceFile: fileInstance
{
CString tempString = [fileInstance GetErrorText];
[self ResetResults];
[self StoreErrorCode: [fileInstance GetErrorCode]
AndText: tempString];
FreeCString(tempString);
[fileInstance free];
return self;
}
- closeDestFile: fileInstance andDelete: (Boolean) deleteit
{
CString tempString = [fileInstance GetErrorText];
[self ResetResults];
if (deleteit == NO)
[fileInstance CloseAndSave];
else
[fileInstance CloseAndDelete];
[self StoreErrorCode: [fileInstance GetErrorCode]
AndText: tempString];
FreeCString(tempString);
[fileInstance free];
return self;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Method: preConversion
// Parameters: none
// Returns: self
// Stores: none
// Description:
// This method is called just before calling the conversion routine (i.e. after
// files have been located and opened alright). It does any work needed
// to finish up preparing for the conversion. At the moment, this means unregistering
// the window, and setting the progress meter up. One is welcome to subclass
// this method, but should probably [super preConversion] before proceeding there.
// Bugs:
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- preConversion
{
[self refuseDragAndDrop];
lastPercent = 0;
[ProgressMeter ActivateWithGoal: 100];
return self;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Method: postConversion
// Parameters: none
// Returns: self
// Stores: none
// Description:
// This method is called just after calling the conversion routine has finished.
// It does some work needed to clean up aspects of this instance after the conversion.
// At the moment, this means re-registering the window for file-icon-dropping,
// and making sure the progress meter shows 100% done. One is welcome to subclass
// this method, but should probably [super postConversion] before proceeding there.
// Bugs:
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- postConversion
{
[self allowDragAndDrop];
[ProgressMeter SetTo: 100];
[ProgressMeter Deactivate];
return self;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Method: CheckFileOK
// Parameters: none
// Returns: self
// Stores: none
// Description:
// This asks the converter if this is an ok file to be converted. If no, then it
// gets the reason why, displays it for the user, and goes into a modal loop to
// force the user to decide whether to continue or not. If the user says continue,
// we continue.
// Bugs:
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- (Boolean) CheckFileOK: fileInst
{
cancelResult = NO;
if ( [converterInst respondsTo:@@selector(isThisAGoodFile:)] )
{
cancelResult = [converterInst isThisAGoodFile: fileInst];
if (cancelResult != YES)
{
[decisionText setStringValue: [converterInst GetCStringFrom: SECOND_RESULT]];
[decisionWindow center];
[decisionWindow makeKeyAndOrderFront: self];
[NXApp runModalFor: decisionWindow];
}
}
else
cancelResult = YES;
return cancelResult;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Method: UserDecided
// Parameters: the thing that called us
// Returns: self
// Stores: none
// Description:
// This method gets called when the user clicks on a button in a window during
// the modal loop set up in CheckFileOK, above. It gets the result and stores it in
// cancelResult, closes the window and shuts down the modal loop.
// Bugs:
// I dislike modality in general.
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- UserDecided: sender
{
if ( [sender tag] == 0)
cancelResult = NO;
else
cancelResult = YES;
[decisionWindow close];
[NXApp stopModal];
return self;
}
/*============================================================*\
At last, here are the true guts of the application. The following three methods
deal with processing files. Briefly, PrepareForConversion: starts from scratch,
and prompts the user for a file to convert from. ConvertThisFile: takes a file name
and determines what file to put the conversion results into. ConvertFrom:To:
then just converts between the two files (this last and init are things that
subclasses MUST override.
\*============================================================*/
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Method: PrepareForConversion:
// Parameters: the object that send this message
// Returns: self
// Stores: error code
// Description:
// This merely creates the converstion necessary object, and sets its
// preference for using curly quotes according to the value stored in this
// object. Then, call the method to start getting information from the user to
// do the conversion.
// Bugs:
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- PrepareForConversion: sender
{
Instance theOpenPanel = [OpenPanel new];
CString const * openedFiles;
roCString theDirectory;
CString fullPath, theFile;
CString tempString;
Integer result, pathLength, index;
[self ResetResults];
//
// Set up the display fields
//
[SourceFileName setStringValue: ""];
[SourcePath setStringValue: ""];
[SourceTitle setStringValue: "Converting:"];
[SourcePathTitle setStringValue: "In:"];
[DestFileName setStringValue: ""];
[DestPath setStringValue: ""];
[DestTitle setStringValue: "To:"];
[DestPathTitle setStringValue: "In:"];
[StatusTitle setStringValue: "Status:"];
[Status setStringValue: ConversionString];
//
// Prepare the open dialog, and get info from the user.
// @@@@ Bug: it doesn't let the user know what kind of conversion
// @@@@ process is now underway in this dialog
//
[cutCommand setEnabled: YES];
[pasteCommand setEnabled: YES];
[spellingCommand setEnabled: YES];
[checkSpellingCommand setEnabled: YES];
[theOpenPanel allowMultipleFiles: YES];
[theOpenPanel setPrompt: SourcePrompt]; // use instance variable for prompt
[theOpenPanel setTitle: "Convert"];
result = [theOpenPanel runModal];
[cutCommand setEnabled: NO];
[pasteCommand setEnabled: NO];
[spellingCommand setEnabled: NO];
[checkSpellingCommand setEnabled: NO];
if (result != 1)
{
[self StoreErrorCode: ERR_USERABORTED
AndText: "User canceled when opening a source file!"];
[Status setStringValue: "Conversion process canceled at your request!"];
}
else
{
//
// get the set of files, as well as the path to them (or it)
//
openedFiles = [theOpenPanel filenames];
theDirectory = [theOpenPanel directory];
pathLength = strlen(theDirectory);
//
// For each of the one or more files, allocate a space for the full path.
// construct the path using the directory name and file name.
// then, call the method to finish up, and dispose of the path we built.
// Note: re: the if-then-else to fuse the file name and path
// If the directory path is not empty, and it doesn't end in a slash, then
// we build the full path from dir/file. Otherwise, do the same without a
// slash. This last is usefull if someone has chosen a file at the root and we
// don't want a double slash (i.e. //myfile). (the check for zero length is really
// just for completeness...)
//
for (index=0; openedFiles[index] != NULL; index++)
{
theFile = openedFiles[index];
fullPath = NewCString(strlen(theFile) + 1 + pathLength);
if ((pathLength > 0) && (theDirectory[pathLength-1] != '/'))
sprintf(fullPath, "%s/%s", theDirectory, theFile);
else
sprintf(fullPath, "%s%s", theDirectory, theFile);
//
// With a path in hand, do the next stage in the converstion.
//
[self ConvertThisFile: fullPath];
FreeCString(fullPath);
}
//
// Check if the file was converted alright...
//
if ([self GetErrorCode] == ERR_OK)
{
[SourceTitle setStringValue: "Converted:"];
[Status setStringValue: "The conversion is done."];
[self StoreErrorCode: ERR_OK AndText: "Conversion process done"];
}
else
{
[SourceTitle setStringValue: "Failed:"];
tempString = [self GetErrorText];
[Status setStringValue: tempString];
FreeCString(tempString);
}
}
return self;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Routine: ConvertThisFile
// Parameters: the full path of the file to be converted.
// Returns: self
// Stores: error code
// Description:
// Given a file, you want to convert it! First, you make sure you can open the file OK,
// then, you ask the user for what the destination file should be called. Having done
// this, you then assure that the destination file can be created without problems,
// and then you ask the converting routine to convert between the two..
// Bugs:
// I find this routine a bit too confusing for my tastes... (long-winded)
// We retrieve some strings below, but can't free them.
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- ConvertThisFile: (roCString) theFile
{
roCString destname;
Instance sourceFile, destFile;
Integer outcome;
PositiveInteger extenLen, destLen;
CString filename, directory, tempstring, basename;
Instance theSavePanel;
Boolean goodfile;
CString tempString;
//
[self ResetResults];
//
// Set up the display fields (we don't always go through prepare for conversion, above.
//
[SourceFileName setStringValue: ""];
[SourcePath setStringValue: ""];
[SourceTitle setStringValue: "Converting:"];
[SourcePathTitle setStringValue: "In:"];
[DestFileName setStringValue: ""];
[DestPath setStringValue: ""];
[DestTitle setStringValue: "To:"];
[DestPathTitle setStringValue: "In:"];
[StatusTitle setStringValue: "Status:"];
[Status setStringValue: ConversionString];
//
// If possible,, open the source file, and update the display.
//
sourceFile = [self openSourceFile: theFile];
if ([self GetErrorCode] != ERR_OK)
{
[SourceFileName setStringValue: theFile];
[SourcePath setStringValue: ""];
tempString = [self GetErrorText];
[Status setStringValue: tempString];
FreeCString(tempString);
}
else
{
goodfile = [self CheckFileOK: sourceFile];
if (goodfile == NO)
{
filename = [sourceFile GetFilename];
directory = [sourceFile GetDirectory];
[SourceFileName setStringValue: filename];
[SourcePath setStringValue: directory];
FreeCString (filename);
FreeCString (directory);
[Status setStringValue: "The conversion was canceled at your request"];
[self StoreErrorCode: ERR_USERABORTED AndText: "The conversion was canceled at your request"];
}
else
{
////
// Get descriptions of the file's name, and use these to build a message
// on the screen for the user, and a prompt in the save panel
////
filename = [sourceFile GetFilename];
directory = [sourceFile GetDirectory];
basename = [sourceFile GetBasename];
//
// Update the user about current events.
//
[SourceFileName setStringValue: filename];
[SourcePath setStringValue: directory];
[Status setStringValue: "OK"];
//
// Then, build a name for the file we want to convert to
//
[cutCommand setEnabled: YES];
[pasteCommand setEnabled: YES];
[spellingCommand setEnabled: YES];
[checkSpellingCommand setEnabled: YES];
theSavePanel = [SavePanel new];
[theSavePanel setPrompt: DestPrompt];
[theSavePanel setTitle: "Convert to"];
tempstring = NewCString(strlen(basename) + strlen(DestExtension));
sprintf(tempstring, "%s%s", basename, DestExtension);
// 93.01.01 djb Added this to free the leak with basename
FreeCString(basename);
outcome = [theSavePanel runModalForDirectory: directory file: tempstring];
FreeCString (tempstring);
FreeCString (filename);
FreeCString (directory);
[cutCommand setEnabled: NO];
[pasteCommand setEnabled: NO];
[spellingCommand setEnabled: NO];
[checkSpellingCommand setEnabled: NO];
////
// Now, check the output of the call to runModalForDirectory:file. If
// the user canceled, tell the user this. Otherwise, continue processing.
////
if (outcome != 1)
{
[self StoreErrorCode: ERR_USERABORTED
AndText: "Conversion process canceled at your request"];
[SourceTitle setStringValue: "Canceled:"];
[Status setStringValue: "Conversion process canceled at your request!"];
}
else
{
////
// Open the destination file. If we can't, display why, otherwise show the
// user what the destination file name and path is. Finally, CONVERT
// the bloody file!
////
//
// See if the file ends with the proper extension. If so, copy it into a
// temporary string. Otherwise, tack the extension onto it...
//
destname = [theSavePanel filename];
extenLen = strlen(DestExtension);
destLen = strlen(destname);
if (strcmp(&destname[destLen - extenLen], DestExtension) == 0)
{
tempstring = NewCString(destLen);
strcpy(tempstring, destname);
}
else
{
tempstring = NewCString(destLen+extenLen);
sprintf(tempstring, "%s%s", destname, DestExtension);
}
//
// Make sure that this ne name doesn't refer to the source file
//
if ([sourceFile SameFileAs: tempstring] == YES)
{
[SourceTitle setStringValue: "Error:"];
[Status setStringValue: "These refer to the same file. This is not allowed!" ];
[self StoreErrorCode: ERR_TRIEDTOOVERWRITE
AndText: "Attempt to overwrite source file with destination file! Conversion NOT done."];
[sourceFile Close];
FreeCString(tempstring);
}
else
{
destFile = [self openDestFile: tempstring];
FreeCString(tempstring);
if ([self GetErrorCode] != ERR_OK)
{
[SourceTitle setStringValue: "Failed:"];
tempString = [self GetErrorText];
[Status setStringValue: tempString];
FreeCString(tempString);
[sourceFile Close];
}
else
{
//
// we opened the dest file. So, tell the user, and continue.
//
filename = [destFile GetFilename];
directory = [destFile GetDirectory];
[DestFileName setStringValue: filename];
[DestPath setStringValue: directory];
FreeCString (filename);
FreeCString (directory);
[self DoConversionFrom: sourceFile To: destFile];
}
}
}
}
}
return self;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Routine: DoConversionFrom:To:
// Parameters: The file to be converted from, and the file to be converted to
// Returns: self
// Stores: none
// Description:
// This is called once we have a source and destination file object opened.
// It does any preparation needed by the application, then calls the convertFrom:To:
// method, and cleans up afterwards.
// Note: tempInst is a kludge that was added for things like rtfConverter which
// destroys the destination file and creates a new one. This allows us to deal with
// this situation.
// Bugs
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- DoConversionFrom: sourceFile To: destinationFile
{
Instance tempInst;
Integer tempError;
CString errorText;
//
// Do some preparing of the conversion window...
//
[self preConversion];
//
// Dim the menus during any conversion process
//
[quitCommand setEnabled: NO];
[hideCommand setEnabled: NO];
[infoCommands setEnabled: NO];
[editCommands setEnabled: NO];
[servicesCommands setEnabled: NO];
[windowsCommands setEnabled: NO];
[Status setStringValue: "Converting"];
tempInst = [self ConvertFrom: sourceFile To: destinationFile];
if (destIsDead == YES)
destinationFile = tempInst;
destIsDead = NO;
[quitCommand setEnabled: YES];
[hideCommand setEnabled: YES];
[infoCommands setEnabled: YES];
[editCommands setEnabled: YES];
[servicesCommands setEnabled: YES];
[windowsCommands setEnabled: YES];
//
// Close the files, now that we are done converting.
//
tempError = [self GetErrorCode];
errorText = [self GetErrorText];
if (tempError < ERR_OK)
[self closeDestFile: destinationFile andDelete: YES];
else
[self closeDestFile: destinationFile andDelete: NO];
[self closeSourceFile: sourceFile];
[self postConversion];
[self StoreErrorCode: tempError AndText: errorText];
FreeCString(errorText);
return self;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Routine: ConvertFrom:To:
// Parameters: The file to be converted from, and the file to be converted to
// Returns: self
// Stores: none
// Description:
// This is a method indended to be subclassed. It would do the actual conversion
// of the source to destination file.
// Bugs
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- ConvertFrom: sourceFile To: destinationFile
{
//
// If our converter instance has the ReportTo: method, then we tell it to report to it
//
if ( [converterInst respondsTo:@@selector(ReportTo:)] )
[converterInst ReportTo: self];
[Status setStringValue: "Converting"];
return self;
}
@@end
@
1.3
log
@Sun Jan 10 15:07:50 PST 1993
@
text
@d81 3
a83 1
// This code for the drag-and-drop was mainly copied from DrawDocument.
d86 2
d91 1
d101 2
a102 1
[self allowDragAndDrop];
d111 2
a112 2
[cutCommand setEnabled: NO];
[pasteCommand setEnabled: NO];
d320 2
d324 128
d466 2
a467 1
// over the application's window.
d469 2
d475 1
d485 4
d500 1
a500 1
// process them.
d502 2
d508 1
d515 7
d866 26
@
1.2
log
@Hopefully a pretty final version (it lived through the Font contrller...
@
text
@d14 1
a14 1
#import <libc.h> //for getwd()
d16 5
a20 2
#import <appkit/Listener.h> // for the drag-and-drop of files facility
#import <appkit/Speaker.h> // ibid
d23 2
d32 2
a33 1
user to double click on a file in the workspace and have it open up.
d37 31
d69 1
d102 8
d175 1
d180 16
d197 1
a197 1
return YES; // Rashly assume that it got converted OK. =)
d396 1
d414 16
a432 1
d438 1
d441 122
d584 4
d589 1
d602 26
a627 1
d633 15
d739 1
a739 1
// in increments of 15%, simply because re-drawing after every percent, for
d745 1
a745 1
if (percentage > lastPercent+15)
d749 1
d767 5
d780 1
a780 1
[fileInstance CreateAndOpenFor: FILE_WRITE];
d823 2
d829 1
a831 1
// [fileInstance Close];
d833 2
a834 1
AndText: [fileInstance GetErrorText]];
d843 1
d851 2
a852 1
AndText: [fileInstance GetErrorText]];
d993 1
d1017 5
d1026 6
d1083 3
a1085 1
[Status setStringValue: [self GetErrorText]];
d1118 1
a1118 2
Integer tempError;
CString errorText;
d1144 3
a1146 1
[Status setStringValue: [self GetErrorText]];
d1153 6
a1158 3
//Bug: we are loosing these strings (cn't free them)
[SourceFileName setStringValue: [sourceFile GetFilename]];
[SourcePath setStringValue: [sourceFile GetDirectory]];
d1180 5
d1190 2
a1191 1
d1197 6
d1258 3
a1260 1
[Status setStringValue: [self GetErrorText]];
a1265 4
// Do some preparing of the conversion window...
//
[self preConversion];
//
d1274 1
a1274 19
[Status setStringValue: "Converting"];
[self ConvertFrom: sourceFile To: destFile];
//
// Close the files, now that we are done converting.
//
tempError = [self GetErrorCode];
errorText = [self GetErrorText];
if (tempError < ERR_OK)
[self closeDestFile: destFile andDelete: YES];
else
[self closeDestFile: destFile andDelete: NO];
[self closeSourceFile: sourceFile];
[self postConversion];
[self StoreErrorCode: tempError AndText: errorText];
FreeCString(errorText);
d1286 63
d1365 1
@
1.1
log
@Initial revision
@
text
@d2 2
d9 1
d14 1
a14 3
#import "File.h"
#import <libc.h> //for getwd
#import <appkit/defaults.h> // for NXApp to get path to app
d16 4
d21 1
d23 1
d25 3
a27 1
@@implementation ConvertController
d29 1
d31 1
d33 1
a33 1
// Routine: appDidInit
d39 5
a43 1
// do some final initialization stuff
d45 1
d49 13
d63 1
d69 301
d376 2
d383 13
d473 1
a473 1
// This method is indended to be overridden. THe subclass would just do whatever
d485 233
d733 1
a733 1
ConstCString theDirectory;
d806 1
a819 25
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Method: ConvertOneFile:OfType:
// Parameters: The name of a file, and its type
// Returns: self
// Stores: nothing
// Description:
// This method is indented mainly to be used for the applicaication delegate to contact
// when the user has double clicked on a file in the workspace. This, in turn, does all
// the work necessary to then proceed and call the routine below to get the name of
// the file to convert to. By default this ignores the type.
// Bugs:
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- ConvertOneFile: (CString) filename OfType: (CString) type
{
[self ResetResults];
//
// Clear the display fields, and call the conversion routine
//
//
// Set up the display fields
//
[SourceFileName setStringValue: ""];
[SourcePath setStringValue: ""];
[SourceTitle setStringValue: "Converting:"];
[SourcePathTitle setStringValue: "In:"];
a820 7
[DestFileName setStringValue: ""];
[DestPath setStringValue: ""];
[DestTitle setStringValue: "To:"];
[DestPathTitle setStringValue: "In:"];
[Status setStringValue: ConversionString];
[StatusTitle setStringValue: "Status:"];
a821 16
[self ConvertThisFile: filename];
//
// Check if the file was converted alright...
//
if ([self GetErrorCode] == ERR_OK)
{
[Status setStringValue: "The conversion is done."];
[self StoreErrorCode: ERR_OK AndText: "Conversion process done"];
}
else
{
[SourceTitle setStringValue: "Failed:"];
[Status setStringValue: [self GetErrorText]];
}
return self;
}
a822 2
a823 26
// Routine: openSource and Dest files
// Parameters: the full path of the file to be converted.
// Returns: the file object
// Stores: none
// Description:
// These tiny methods are provided so a subclass can override what kind of
// file gets opened for the source or the dest, without overriding the whole
// of the conversion methods. Note that they should always open a subclass
// of File, though.
// Bugs:
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- openDestFile: (CString) theFile
{
return [[File alloc] initAndUse: theFile];
}
- openSourceFile: (CString) theFile
{
return [[File alloc] initAndUse: theFile];
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
d834 2
a835 1
// I find this routine a bit too confusing for my tastes...
d837 1
a837 1
- ConvertThisFile: (CString) theFile
d839 1
a839 1
ConstCString destname;
d845 3
d851 15
d869 1
a869 2
[sourceFile OpenExistingFor: FILE_READ];
if ([sourceFile GetErrorCode] != ERR_OK)
d873 1
a873 1
[Status setStringValue: [sourceFile GetErrorText]];
d877 2
a878 32
////
// Get descriptions of the file's name, and use these to build a message
// on the screen for the user, and a prompt in the save panel
////
filename = [sourceFile GetFilename];
directory = [sourceFile GetDirectory];
basename = [sourceFile GetBasename];
//
// Update the user about current events.
//
[SourceFileName setStringValue: filename];
[SourcePath setStringValue: directory];
[Status setStringValue: "OK"];
//
// Then, build a name for the file we want to convert to
//
theSavePanel = [SavePanel new];
[theSavePanel setPrompt: DestPrompt];
[theSavePanel setTitle: "Convert to"];
tempstring = NewCString(strlen(basename) + strlen(DestExtension));
sprintf(tempstring, "%s%s", basename, DestExtension);
outcome = [theSavePanel runModalForDirectory: directory file: tempstring];
FreeCString (tempstring);
FreeCString (filename);
FreeCString (directory);
////
// Now, check the output of the call to runModalForDirectory:file. If
// the user canceled, tell the user this. Otherwise, continue processing.
////
if (outcome != 1)
d880 5
a884 4
[self StoreErrorCode: ERR_OK
AndText: "The user chose to abort the conversion at this point"];
[SourceTitle setStringValue: "Canceled:"];
[Status setStringValue: "Conversion process canceled at your request!"];
d889 2
a890 3
// Open the destination file. If we can't, display why, otherwise show the
// user what the destination file name and path is. Finally, CONVERT
// the bloody file!
d892 3
d896 1
a896 2
// See if the file ends with the proper extension. If so, copy it into a
// temporary string. Otherwise, tack the extension onto it...
d898 3
a900 13
destname = [theSavePanel filename];
extenLen = strlen(DestExtension);
destLen = strlen(destname);
if (strcmp(&destname[destLen - extenLen], DestExtension) == 0)
{
tempstring = NewCString(destLen);
strcpy(tempstring, destname);
}
else
{
tempstring = NewCString(destLen+extenLen);
sprintf(tempstring, "%s%s", destname, DestExtension);
}
d902 1
a902 1
// Make sure that this ne name doesn't refer to the source file
d904 16
a919 1
if ([sourceFile SameFileAs: tempstring] == YES)
d921 4
a924 6
[SourceTitle setStringValue: "Error:"];
[Status setStringValue: "These refer to the same file. This is not allowed!" ];
[self StoreErrorCode: ERR_TRIEDTOOVERWRITE
AndText: "Attempt to overwrite source file with destination file! Conversion NOT done."];
[sourceFile Close];
d928 13
a940 3
destFile = [self openDestFile: tempstring];
[destFile CreateAndOpenFor: FILE_WRITE];
if ([destFile GetErrorCode] != ERR_OK)
d942 18
a959 2
[SourceTitle setStringValue: "Failed:"];
[Status setStringValue: [destFile GetErrorText]];
d961 1
d965 23
a987 6
free(tempstring); // not needed anymore.
//
// we opened the dest file. So, tell the user, and continue.
//
filename = [destFile GetFilename];
directory = [destFile GetDirectory];
d989 12
a1000 2
[DestFileName setStringValue: filename];
[DestPath setStringValue: directory];
d1002 6
a1007 12
[Status setStringValue: "Converting"];
[self ConvertFrom: sourceFile To: destFile];
FreeCString (filename);
FreeCString (tempstring);
FreeCString (directory);
//
// Convert the file and then clean up.
//
[destFile CloseAndSave];
[sourceFile Close];
d1029 5
@